#include "SWFInputStream.h"
#include "SWF.h"
#include <assert.h>

SWFInputStream::SWFInputStream( char* data , size_t length)
	: _data(data)
	, _length(length)
	, _position(0)
	, _bitPosition(8)
	, _bitTemp(0)
{

}

SWFInputStream::SWFInputStream()
	: _data(nullptr)
	, _length(0)
	, _position(0)
	, _bitPosition(8)
	, _bitTemp(0)
{

}

Rect SWFInputStream::readRect()
{
	this->alignBits();

	Rect r;

	r.NBits = this->readUB(5);
	r.Xmin = this->readSB(r.NBits);
	r.Xmax = this->readSB(r.NBits);
	r.Ymin = this->readSB(r.NBits);
	r.Ymax = this->readSB(r.NBits);

	return r;
}

UI8 SWFInputStream::readUI8()
{
	static int _UI8_size = sizeof(UI8);
	assert(_length >= _position + _UI8_size);

	UI8 v = 0;
	memcpy(&v, _data + _position, _UI8_size);
	_position += _UI8_size;
	return v;
}

UI16 SWFInputStream::readUI16()
{
	this->alignBits();
	UI16 v = 0;
	memcpy(&v, _data + _position, sizeof(UI16));
	_position += sizeof(UI16);
	v = reverse_2(v);
	return v;
}

SI24 SWFInputStream::readS24()
{
	SI24 v = 0;
	UI8 b1 = this->readUI8();
	UI8 b2 = this->readUI8();
	UI8 b3 = this->readUI8();
	v = (b1 << 16) | (b2 << 8) | b3;
	return v;
}

UI8 SWFInputStream::readU8()
{
	return this->readUI8();
}

UI30 SWFInputStream::readU30()
{
	UI30 v = 0;
	for (int i = 0; i < 5; ++i)
	{
		UI8 bit = this->readUI8();
		bool hasNext = (bit & 0x80) == 0x80;
		UI8 bitValue = (bit & 0x7F);
		v = (bitValue << 7 * i) | v;
		if (hasNext == false) break;
	}
	return v;
}

UI32 SWFInputStream::readU32()
{
	UI32 v = 0;
	for (int i = 0; i < 5; ++i)
	{
		UI8 bit = this->readUI8();
		bool hasNext = (bit & 0x80) == 0x80;
		UI8 bitValue = (bit & 0x7F);
		v = (bitValue << 7 * i) | v;
		if (hasNext == false) break;
	}
	return v;
}

UI32 SWFInputStream::readUI32()
{
	UI32 v = 0;
	memcpy(&v, _data + _position, sizeof(UI32));
	_position += sizeof(UI32);
	v = reverse_4(v);
	return v;
}

SI8 SWFInputStream::readSI8()
{
	assert(false);
	return 0;
}

SI16 SWFInputStream::readSI16()
{
	return readUI16();
}

SI32 SWFInputStream::readS32()
{
	SI32 val = readUI8() | (readUI8() << 8) |(readUI8() << 16) | (readUI8() << 24);
	return val;
}

UB SWFInputStream::readUB(unsigned int size)
{
	UB value = 0;
	while (size)
	{
		if (_bitPosition == 8)
		{
			_bitPosition = 0;
			_bitTemp = this->readUI8();
		}

		char bit = _bitTemp >> (7 - _bitPosition);
		bit = bit & 1;

		value = (value << 1) | bit;

		size--;
		_bitPosition++;
	}

	return value;
}

SB SWFInputStream::readSB(unsigned int size)
{
	assert(size <= 32);
	int num = readUB(size);
	int shift = 32 - size;
	// sign extension
	num = (num << shift) >> shift;
	return num;
}

FB SWFInputStream::readFB(unsigned int size)
{
	return readSB(size);
}

Fixed8 SWFInputStream::readFixed8()
{
	int val = readUI16();
	return (FLOAT)(val / 256.0f);
}

Fixed SWFInputStream::readFixed()
{
	SI32 val = readS32();
	return (Fixed)(val / 65536.0f);
}

FLOAT SWFInputStream::readFloat()
{
	assert(false);
	return 0;
}

DOUBLE SWFInputStream::readDouble()
{
	DOUBLE v = 0;
	memcpy(&v, _data + _position, sizeof(DOUBLE));
	_position += sizeof(DOUBLE);
	//v = reverse_4(v);
	return v;
}

void SWFInputStream::readBytes( char* dest, int len )
{
	memcpy(dest, _data + _position, len);
	_position += len;
}

void SWFInputStream::alignBits()
{
	_bitPosition = 8;
	_bitTemp = 0;
}

SWFInputStream::~SWFInputStream()
{
	//if (_data)
	//	free(_data);
}

std::string SWFInputStream::readString()
{
	std::string str;
	while (this->getRemain() > 0)
	{
		char c = _data[_position++];
		if (c == 0)
			break;
		else
			str += c;
	}
	return str;
}

std::string SWFInputStream::readString(UI8 size)
{
	char* strData = (char*) malloc(size + 1);
	strData[size] = 0;
	this->readBytes(strData, size);
	std::string str = strData;
	free(strData);

	return str;
}

std::string SWFInputStream::readUTFString()
{
	UI30 size = this->readU30();
	char* strData = (char*) malloc(size + 1);
	strData[size] = 0;
	this->readBytes(strData, size);
	std::string str = strData;
	free(strData);

	return str;
}

void SWFInputStream::setData( char* data, size_t len )
{
	this->_data = data;
	this->_length = len;
	this->alignBits();
	_position = 0;
}

void SWFInputStream::readByteArray( ByteArray* barray, size_t length )
{
	barray->copy(_data + _position, length, barray->getLength());
	_position += length;
}

ColorRGB SWFInputStream::readColorRGB()
{
	UI8 Red = readUI8();
	UI8 Green = readUI8();
	UI8 Blue = readUI8();

	ColorRGB rgb = (Red << 16) | (Green << 8) | Blue;
	return rgb;
}

ColorRGBA SWFInputStream::readColorRGBA()
{
	UI8 Red = readUI8();
	UI8 Green = readUI8();
	UI8 Blue = readUI8();
	UI8 Alpha = readUI8();

	ColorRGBA rgba = (Red << 24) | (Green << 16) | (Blue << 8) | Alpha;
	return rgba;
}

Matrix SWFInputStream::readMatrix()
{
	this->alignBits();

	Matrix m;
	m.HasScale = readUB(1);
	if (m.HasScale == 1)
	{
		m.NScaleBits = readUB(5);
		m.ScaleX = readFB(m.NScaleBits);
		m.ScaleY = readFB(m.NScaleBits);
	}

	m.HasRotate = readUB(1);
	if (m.HasRotate == 1)
	{
		m.NRotateBits = readUB(5);
		m.RotateSkew0 = readFB(m.NRotateBits);
		m.RotateSkew1 = readFB(m.NRotateBits);
	}

	m.NTranslateBits = readUB(5);
	m.TranslateX = readSB(m.NTranslateBits);
	m.TranslateY = readSB(m.NTranslateBits);

	return m;
}

CXFORM* SWFInputStream::readColorTransform()
{
	this->alignBits();
	CXFORM* ct = new CXFORM();
	ct->HasAddTerms = readUB(1);
	ct->HasMultTerms = readUB(1);
	ct->Nbits = readUB(4);
	if (ct->HasMultTerms == 1)
	{
		ct->RedMultTerm = readSB(ct->Nbits);
		ct->GreenMultTerm = readSB(ct->Nbits);
		ct->BlueMultTerm = readSB(ct->Nbits);
	}
	if (ct->HasAddTerms == 1)
	{
		ct->RedAddTerm = readSB(ct->Nbits);
		ct->GreenAddTerm = readSB(ct->Nbits);
		ct->BlueAddTerm = readSB(ct->Nbits);
	}
	return ct;
}

CXFORMWITHALPHA* SWFInputStream::readColorTransformWithAlpha()
{
	this->alignBits();
	CXFORMWITHALPHA* ct = new CXFORMWITHALPHA();
	ct->HasAddTerms = readUB(1);
	ct->HasMultTerms = readUB(1);
	ct->Nbits = readUB(4);
	if (ct->HasMultTerms == 1)
	{
		ct->RedMultTerm = readSB(ct->Nbits);
		ct->GreenMultTerm = readSB(ct->Nbits);
		ct->BlueMultTerm = readSB(ct->Nbits);
		ct->AlphaMultTerm = readSB(ct->Nbits);
	}
	if (ct->HasAddTerms == 1)
	{
		ct->RedAddTerm = readSB(ct->Nbits);
		ct->GreenAddTerm = readSB(ct->Nbits);
		ct->BlueAddTerm = readSB(ct->Nbits);
		ct->AlphaAddTerm = readSB(ct->Nbits);
	}
	return ct;
}
